/*
 * mftgslicerenderer.cpp
 *
 *  Created on: 23/07/2012
 *      Author: ccve
 *
 * Multi-Field Texture Grammar Slice Renderer
 *
 * Renders slices of multiple volumes using our multi-field texture grammar method.
 */

#include "mftgslicerenderer.h"

#include "texturegrammar/node.h"
#include "texturegrammar/nodelist.h"

#include "modules/flowreen/datastructures/flow2d.h"

#include "helpers/helper.h"

#include <stdio.h>
#include <stdlib.h>

/**
 * TODO Have a look at MultiVolumeProxyGeometry for a multi input for volumes..
 * 		We should use either that, or a VolumeCollection input.
 */
namespace voreen {

MFTGSliceRenderer::MFTGSliceRenderer()
    : ImageProcessor("texgramshader")
	, outport_(Port::OUTPORT, "outport")
	, privatePort1_(Port::OUTPORT, "image.temp1", false)
	, refreshRate("refreshRate", "Refresh rate: ", 5, 1, 15)
	, ORIGINAL_COUNTER(3)
	, counter_(3)
	, noisemap(0)
	, data1(0)
	, data2(0)
	, data3(0)
	, data4(0)
	, data5(0)
	, data6(0)
	, data7(0)
	, data8(0)
	, data9(0)
	, data10(0)
	, count_scalar_fields(0)
	, count_vector_fields(0)
{
    addPort(outport_);
    addPrivateRenderPort(&privatePort1_);

    addProperty(refreshRate);

    mouseEventProp_ = new EventProperty<MFTGSliceRenderer>(
        "mouseEvent.measure", "Distance measure", this,
        &MFTGSliceRenderer::mouseEvent,
        tgt::MouseEvent::MOUSE_BUTTON_LEFT,
        tgt::MouseEvent::MOTION | tgt::MouseEvent::PRESSED | tgt::MouseEvent::RELEASED ,
        tgt::Event::MODIFIER_NONE, false);
    addEventProperty(mouseEventProp_);
    mouseDown_ = false;
    mouseCurPos2D_ = tgt::ivec2(0, 0);

    mouseEventProp2_ = new EventProperty<MFTGSliceRenderer>(
        "mouseEvent.rightclick", "Rightclick event", this,
        &MFTGSliceRenderer::rightClickEvent,
        tgt::MouseEvent::MOUSE_BUTTON_RIGHT,
        tgt::MouseEvent::RELEASED ,
        tgt::Event::MODIFIER_NONE, false);
    addEventProperty(mouseEventProp2_);

    // TODO fix file location problems
    grammar_ = new TextureGrammar("C:/Users/cvan680/myfirstgrammar.teg");
    grammar_->run();

    // hack: change when changing total # fields
    total_fields = grammar_->getNumberOfFields();;
    current_field = 1;


}
MFTGSliceRenderer::~MFTGSliceRenderer(){
	delete noisemap, data1, data2, data3, data4, data5, data6, data7, data8, data9, data10;
	delete grammar_;
	delete mouseEventProp_, mouseEventProp2_;
}

void MFTGSliceRenderer::process(){
    if (getInvalidationLevel() >= Processor::INVALID_PROGRAM){ compile(); }

    if(!grammar_->completed()){ grammar_->run(); total_fields = grammar_->getNumberOfFields(); }

    ORIGINAL_COUNTER = refreshRate.get();

	tgt::ivec2 viewportSize = outport_.getSize();
	int numPix = viewportSize.x * viewportSize.y;

	int t1 = total_fields >= 1 ? grammar_->getNode(0).uniqueID_ : 0;
	int t2 = total_fields >= 2 ? grammar_->getNode(2).uniqueID_ : 0;
	int t3 = total_fields >= 3 ? grammar_->getNode(4).uniqueID_ : 0;
	int t4 = total_fields >= 4 ? grammar_->getNode(6).uniqueID_ : 0;
	int t5 = total_fields >= 5 ? grammar_->getNode(8).uniqueID_ : 0;
	int t6 = total_fields >= 6 ? grammar_->getNode(10).uniqueID_ : 0;
	int t7 = total_fields >= 7 ? grammar_->getNode(12).uniqueID_ : 0;
	int t8 = total_fields >= 8 ? grammar_->getNode(14).uniqueID_ : 0;
	int t9 = total_fields >= 9 ? grammar_->getNode(16).uniqueID_ : 0;
	int t10 = total_fields >= 10 ? grammar_->getNode(18).uniqueID_ : 0;
	int c1 = total_fields >= 2 ? grammar_->getNode(1).uniqueID_ : 0;
	int c2 = total_fields >= 3 ? grammar_->getNode(3).uniqueID_ : 0;
	int c3 = total_fields >= 4 ? grammar_->getNode(5).uniqueID_ : 0;
	int c4 = total_fields >= 5 ? grammar_->getNode(7).uniqueID_ : 0;
	int c5 = total_fields >= 6 ? grammar_->getNode(9).uniqueID_ : 0;
	int c6 = total_fields >= 7 ? grammar_->getNode(11).uniqueID_ : 0;
	int c7 = total_fields >= 8 ? grammar_->getNode(13).uniqueID_ : 0;
	int c8 = total_fields >= 9 ? grammar_->getNode(15).uniqueID_ : 0;
	int c9 = total_fields >= 10 ? grammar_->getNode(17).uniqueID_ : 0;

	prepareData(viewportSize);

	outport_.activateTarget();
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glDepthFunc(GL_ALWAYS);


	// initialize shader
	program_->activate();
	program_->setUniform("vpdims_", static_cast<tgt::vec2>(viewportSize));
    program_->setUniform("mouseDown_", mouseDown_);
    program_->setUniform("mouseCoord_", static_cast<tgt::vec2>(mouseCurPos2D_));
    program_->setUniform("current_field_", current_field);

    // set the total number of fields, the technique ID and comp ID for each field
    program_->setUniform("number_of_fields", total_fields);

    program_->setUniform("tech1", t1);
    program_->setUniform("tech2", t2);
    program_->setUniform("tech3", t3);
    program_->setUniform("tech4", t4);
    program_->setUniform("tech5", t5);
    program_->setUniform("tech6", t6);
    program_->setUniform("tech7", t7);
    program_->setUniform("tech8", t8);
    program_->setUniform("tech9", t9);
    program_->setUniform("tech10", t10);
    program_->setUniform("comp1", c1);
    program_->setUniform("comp2", c2);
    program_->setUniform("comp3", c3);
    program_->setUniform("comp4", c4);
    program_->setUniform("comp5", c5);
    program_->setUniform("comp6", c6);
    program_->setUniform("comp7", c7);
    program_->setUniform("comp8", c8);
    program_->setUniform("comp9", c9);

	// second: create all textures (scalar, vector, etc) (to make sure the shader sees a sampler2D for each field even if we don't have that field,
    // upload a dummy noisemap to each uniform sampler2D that doesn't have a field assigned.
    GLuint idNoise, id1, id2, id3, id4, id5, id6, id7, id8, id9, id10;
	idNoise = createTexture(noisemap, viewportSize, TexType::SCALAR);
	if(total_fields >= 1){ id1 = createTexture(data1, viewportSize, textype1); }else{ id1 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 2){ id2 = createTexture(data2, viewportSize, textype2); }else{ id2 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 3){ id3 = createTexture(data3, viewportSize, textype3); }else{ id3 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 4){ id4 = createTexture(data4, viewportSize, textype4); }else{ id4 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 5){ id5 = createTexture(data5, viewportSize, textype5); }else{ id5 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 6){ id6 = createTexture(data6, viewportSize, textype6); }else{ id6 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 7){ id7 = createTexture(data7, viewportSize, textype7); }else{ id7 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 8){ id8 = createTexture(data8, viewportSize, textype8); }else{ id8 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 9){ id9 = createTexture(data9, viewportSize, textype9); }else{ id9 = createTexture(noisemap, viewportSize, TexType::SCALAR); }
	if(total_fields >= 10){ id10 = createTexture(data10, viewportSize, textype10); }else{ id10 = createTexture(noisemap, viewportSize, TexType::SCALAR); }

	// third: bind all textures
	TextureUnit* tuNoise = new TextureUnit();
	TextureUnit* tu1 = new TextureUnit();
	TextureUnit* tu2 = new TextureUnit();
	TextureUnit* tu3 = new TextureUnit();
	TextureUnit* tu4 = new TextureUnit();
	TextureUnit* tu5 = new TextureUnit();
	TextureUnit* tu6 = new TextureUnit();
	TextureUnit* tu7 = new TextureUnit();
	TextureUnit* tu8 = new TextureUnit();
	TextureUnit* tu9 = new TextureUnit();
	TextureUnit* tu10 = new TextureUnit();

	bindTexture(idNoise, "noisemap_", tuNoise); // scalar noise (for use w LIC e.g.)
	bindTexture(id1, "data1_", tu1);
	bindTexture(id2, "data2_", tu2);
	bindTexture(id3, "data3_", tu3);
	bindTexture(id4, "data4_", tu4);
	bindTexture(id5, "data5_", tu5);
	bindTexture(id6, "data6_", tu6);
	bindTexture(id7, "data7_", tu7);
	bindTexture(id8, "data8_", tu8);
	bindTexture(id9, "data9_", tu9);
	bindTexture(id10,"data10_",tu10);

    setGlobalShaderParameters(program_);

    renderQuad();

    glDepthFunc(GL_LESS);

    program_->deactivate();
    outport_.deactivateTarget();
    TextureUnit::setZeroUnit(); // TODO what is this one for?
    LGL_ERROR;

    delete tuNoise, tu1, tu2, tu3, tu4, tu5, tu6, tu7, tu8, tu9, tu10;

}

GLuint MFTGSliceRenderer::createTexture(GLubyte* data, tgt::ivec2 dims, int textype){
	GLint internalformat;
	GLenum format;
	if(textype == TexType::SCALAR){ internalformat = GL_LUMINANCE; format = GL_LUMINANCE; }
	else if(textype == TexType::VECTOR){ internalformat = GL_RGB16; format = GL_RGB; }
	else if(textype == TexType::COLOUR){ internalformat = GL_RGBA16; format = GL_RGBA; }

	GLuint id = 0;
	glGenTextures(1, &id);
    glBindTexture(GL_TEXTURE_2D, id);
    glTexImage2D(GL_TEXTURE_2D, 0, internalformat, dims.x, dims.y, 0, format, GL_FLOAT, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    return id;
}
void MFTGSliceRenderer::bindTexture(GLuint id, string uniform, TextureUnit texunit){
    glActiveTexture(texunit.getEnum());
    glBindTexture(GL_TEXTURE_2D, id);
	program_->setUniform(uniform, texunit.getUnitNumber());
}

/* Bunch of standard functions */
Processor* MFTGSliceRenderer::create() const { return new MFTGSliceRenderer(); }
void MFTGSliceRenderer::initialize() throw (VoreenException) { ImageProcessor::initialize(); }
void MFTGSliceRenderer::deinitialize() throw (VoreenException) { ImageProcessor::deinitialize(); }



/**
 * vps = viewportsize
 */
void MFTGSliceRenderer::prepareData(tgt::ivec2 vps){

	//FIXME implement this properly!

	//FIXME ask texturegrammar how many fields we need to upload;
	//		then ask of what type each field should be

	if(!grammar_->completed()){ grammar_->run(); total_fields = grammar_->getNumberOfFields(); }

	if(!noisemap){ noisemap = noiseMap(vps); }

	count_scalar_fields = 0;
	count_vector_fields = 0;

	if(total_fields >= 1 && !data1){
		data1 = getData(1, vps);
		textype1 = grammar_->getNode(0).textype_;
	}
	if(total_fields >= 2 && !data2){
		data2 = getData(2, vps);
		textype2 = grammar_->getNode(2).textype_;
	}
	if(total_fields >= 3 && !data3){
		data3 = getData(3, vps);
		textype3 = grammar_->getNode(4).textype_;
	}
	if(total_fields >= 4 && !data4){
		data4 = getData(4, vps);
		textype4 = grammar_->getNode(6).textype_;
	}
	if(total_fields >= 5 && !data5){
		data5 = getData(5, vps);
		textype5 = grammar_->getNode(8).textype_;
	}
	if(total_fields >= 6 && !data6){
		data6 = getData(6, vps);
		textype6 = grammar_->getNode(10).textype_;
	}
	if(total_fields >= 7 && !data7){
		data7 = getData(7, vps);
		textype7 = grammar_->getNode(12).textype_;
	}
	if(total_fields >= 8 && !data8){
		data8 = getData(8, vps);
		textype8 = grammar_->getNode(14).textype_;
	}
	if(total_fields >= 9 && !data9){
		data9 = getData(9, vps);
		textype9 = grammar_->getNode(16).textype_;
	}
	if(total_fields >= 10 && !data10){
		data10 = getData(10, vps);
		textype10 = grammar_->getNode(18).textype_;
	}

}

GLubyte* MFTGSliceRenderer::getData(int fieldindex, tgt::ivec2 vps){

	// fieldindex in [1..10], node index would be 0, 2, 4, 6, 8, 10...
	// (for now, even numbers are techniques, odd numbers are compositing methods)
	int nodeindex = (fieldindex - 1) * 2;
	TechniqueWrapper tw = grammar_->getNode(nodeindex);
	switch(tw.textype_){
	case TexType::SCALAR:
		if(tw.uniqueID_ == TechniqueCodes::VF_STREAMLINES){ return TEMPstreamLines(vps); }
		else{
			count_scalar_fields += 1;
			return scalarField(vps, count_scalar_fields);
		}
		break;
	case TexType::VECTOR:
		count_vector_fields += 1;
		return vectorField(vps, count_vector_fields);
		break;
	case TexType::COLOUR:
		if(tw.uniqueID_ == TechniqueCodes::VF_ARROWPLOT){ return arrowPlot(vps); }
		break;
	}
	count_scalar_fields += 1;
	std::cout << "\n----------------------------------------------------\n";
	std::cout << "- ENCOUNTERED PROBLEM, COULD NOT IDENTIFY FIELD " << fieldindex << " -";
	std::cout << "\n----------------------------------------------------\n";
	return scalarField(vps, count_scalar_fields); // if unknown, just return a scalarfield..
}





/*********************************************************************************
 * BELOW THIS LINE ARE METHODS CREATING SYNTHETIC 2D DATASETS, SCALAR AND VECTOR *
 *********************************************************************************/

/**
 * Noise scalar field
 */
GLubyte* MFTGSliceRenderer::noiseMap(tgt::vec2 dim){
	int numPix = dim.x*dim.y;
	float* data = new float[numPix];
	memset(data, 0, sizeof(float) * numPix);
	for(int i=0; i<numPix; i++){
		data[i] = (float)rand()/(float)RAND_MAX;
	}
	GLubyte* result = reinterpret_cast<GLubyte*>(data);
	//delete data;
	return result;
}

/**
 * Simple scalarfield with luminance increasing from bottom to top
 */
GLubyte* MFTGSliceRenderer::scalarField(tgt::vec2 dim, int whichfield){

	int numPix = dim.x*dim.y;
	float* data = new float[numPix];
	memset(data, 0, sizeof(float) * numPix);


	tgt::vec2 diff, c, c2;
	float dx, dy, dx2, dy2, dist, dist2;
	int index;

	if(whichfield == 1){ // double radius
		c = tgt::vec2(dim.x*0.40, dim.y*0.70);
		c2 = tgt::vec2(dim.x*0.28, dim.y*0.36);

		float maxdist = 0;
		for(int x=0; x<dim.x; ++x){
			for(int y=0; y<dim.y; ++y){
				dx = c.x - x;
				dy = c.y - y;
				dx2 = c2.x - x;
				dy2 = c2.y - y;
				dist = sqrtf(dx*dx+dy*dy);
				dist2 = sqrtf(dx2*dx2+dy2*dy2);
				if(dist2 < dist){ dist = dist2; }
				if(dist > maxdist){ maxdist = dist; }
				index = y*dim.x + x;
				data[index] = dist;
			}
		}
		for(int i=0; i<numPix; ++i){ data[i] = (maxdist - data[i]) / maxdist; }
	}else if(whichfield == 2){ // radial
		c = tgt::vec2(dim.x/(float)2.0, dim.y/(float)2.0);
		for(int x=0; x<dim.x; ++x){
			for(int y=0; y<dim.y; ++y){
				dx = c.x - x;
				dy = c.y - y;
				dist = sqrtf(dx*dx+dy*dy);
				index = y*dim.x + x;
				data[index] = dist / c.x;
			}
		}
	}else if(whichfield == 3){ // linear
		for(int i=0; i<numPix; i++){ data[i] = (float)i/numPix; }
	}else{ // just do linear
		for(int i=0; i<numPix; i++){ data[i] = (float)i/numPix; }
	}

	GLubyte* result = reinterpret_cast<GLubyte*>(data);
	//delete data;
	return result;
}

/*****************
 * VECTOR FIELDS *
 *****************/
GLubyte* MFTGSliceRenderer::vectorField(tgt::vec2 dim, int whichfield){
	if(whichfield == 1){
		return vectorField01(dim);
	}else if(whichfield == 2){
		return vectorField02(dim);
	}

	// default in case of wrong fieldindex
	return vectorFieldRandom(dim);
}

GLubyte* MFTGSliceRenderer::vectorField01(tgt::vec2 dim){
	int numPix = dim.x*dim.y;
	tgt::vec3* data = new tgt::vec3[numPix];
	memset(data, 0, numPix * sizeof(tgt::vec3));

	// to copy method and only change singularities, only the next two lines need
	// to be changed, as well as a few lines in the for loop (see comment there).
	tgt::vec2 s1(dim.x*0.6, dim.y*0.6);
	tgt::vec2 s2(dim.x*0.2, dim.y*0.3);

	float maxv = 0;
	float minv = 10000;
	float len;

	tgt::vec2 value, v1, v2;
	int index;

	for(int x=0; x<dim.x; x++){
		for(int y=0; y<dim.y; y++){
			v1 = tgt::vec2(s1.x-x, s1.y-y);
			v2 = tgt::vec2(s2.x-x, s2.y-y);

			// to copy method and only change singularities, only the next few lines need
			// to be changed, as well as a few lines in the for loop (see comment there).
			value = tgt::vec2(0.0, 0.0);
			value += calcVec(v1, NODE);
			value += calcVec(v2, ATTRACTING_FOCUS);

			len = sqrtf(tgt::dot(value, value));
			if(len > maxv){ maxv = len; } if(len < minv){ minv = len; }

			index = y*dim.x + x;
			data[index] = tgt::vec3(value, 0.0);
		}
	}

	// normalize flow
	float range = maxv - minv;
	for(int i = 0; i < numPix; ++i){ data[i] = tgt::vec3((data[i].x-minv)/range, (data[i].y-minv)/range, 0.0); }
	GLubyte* result = reinterpret_cast<GLubyte*>(data);
	//delete data;
	return result;
}
GLubyte* MFTGSliceRenderer::vectorField02(tgt::vec2 dim){
	int numPix = dim.x*dim.y;
	tgt::vec3* data = new tgt::vec3[numPix];
	memset(data, 0, numPix * sizeof(tgt::vec3));

	// to copy method and only change singularities, only the next two lines need
	// to be changed, as well as a few lines in the for loop (see comment there).
	tgt::vec2 s1(dim.x*0.15, dim.y*0.85);
	tgt::vec2 s2(dim.x*0.8, dim.y*0.3);

	float maxv = 0;
	float minv = 10000;
	float len;

	tgt::vec2 value, v1, v2;
	int index;

	for(int x=0; x<dim.x; x++){
		for(int y=0; y<dim.y; y++){
			v1 = tgt::vec2(s1.x-x, s1.y-y);
			v2 = tgt::vec2(s2.x-x, s2.y-y);

			// to copy method and only change singularities, only the next few lines need
			// to be changed, as well as a few lines in the for loop (see comment there).
			value = tgt::vec2(0.0, 0.0);
			value += calcVec(v1, SADDLE);
			value += calcVec(v2, CENTER);

			len = sqrtf(tgt::dot(value, value));
			if(len > maxv){ maxv = len; } if(len < minv){ minv = len; }

			index = y*dim.x + x;
			data[index] = tgt::vec3(value, 0.0);
		}
	}

	// normalize flow
	float range = maxv - minv;
	for(int i = 0; i < numPix; ++i){ data[i] = tgt::vec3((data[i].x-minv)/range, (data[i].y-minv)/range, 0.0); }
	GLubyte* result = reinterpret_cast<GLubyte*>(data);
	//delete data;
	return result;
}
GLubyte* MFTGSliceRenderer::vectorFieldRandom(tgt::vec2 dim){
	int numPix = dim.x*dim.y;
	tgt::vec3* data = new tgt::vec3[numPix];
	memset(data, 0, numPix * sizeof(tgt::vec3));

	// to copy method and only change singularities, only the next two lines need
	// to be changed, as well as a few lines in the for loop (see comment there).
	int nums = 1 + ceil( (rand()/static_cast<float>(RAND_MAX)) * 3.0);
	tgt::vec2* sings = new tgt::vec2[nums];
	int* stypes = new int[nums];
	for(int i=0; i<nums; i++){
		sings[i] = tgt::vec2(dim.x*(rand()/static_cast<float>(RAND_MAX)), dim.y*(rand()/static_cast<float>(RAND_MAX)));
		stypes[i] = floor( (rand()/static_cast<float>(RAND_MAX)) * 6.0);
	}
	float maxv = 0;
	float minv = 10000;
	float len;

	tgt::vec2 value;
	int index;

	for(int x=0; x<dim.x; x++){
		for(int y=0; y<dim.y; y++){
			value = tgt::vec2(0.0, 0.0);
			for(int i=0; i<nums; ++i){
				value += calcVec(tgt::vec2(sings[i].x-x, sings[i].y-y), stypes[i]);
			}

			len = sqrtf(tgt::dot(value, value));
			if(len > maxv){ maxv = len; } if(len < minv){ minv = len; }

			index = y*dim.x + x;
			data[index] = tgt::vec3(value, 0.0);
		}
	}

	// normalize flow
	float range = maxv - minv;
	for(int i = 0; i < numPix; ++i){ data[i] = tgt::vec3((data[i].x-minv)/range, (data[i].y-minv)/range, 0.0); }
	GLubyte* result = reinterpret_cast<GLubyte*>(data);
	//delete data;
	return result;
}


tgt::vec2 MFTGSliceRenderer::calcVec(tgt::vec2 rc, int type){
	float decay = 0.000125;
	float angular_velocity = 0.15;

	float dist = sqrtf(tgt::dot(rc, rc));
	float factor = exp(-1 * decay * (dist*dist));

	float xval = 0.0;
	float yval = 0.0;
	switch(type){
		case NODE:
			xval = factor * rc.x;
			yval = factor * rc.y;
			break;
		case SADDLE:
			xval = factor * rc.x;
			yval = factor * - rc.y;
			break;
		case ATTRACTING_FOCUS:
			xval = factor * angular_velocity * (-1*rc.y - .5*rc.x);
			yval = factor * angular_velocity * (rc.x - .5*rc.y);
			break;
		case REPELLING_FOCUS:
			xval = factor * angular_velocity * (-1*rc.y + .5*rc.x);
			yval = factor * angular_velocity * (rc.x + .5*rc.y);
			break;
		case CENTER:
			xval = factor * (-1.0 * angular_velocity * rc.y);
			yval = factor * (angular_velocity * rc.x);
			break;
		case NONE:
		default:
			break;
	}

	return tgt::vec2( xval, yval );
}

GLubyte* MFTGSliceRenderer::arrowPlot(tgt::vec2 dim){
	int numPix = dim.x*dim.y;
	tgt::vec2* data = new tgt::vec2[numPix];
	memset(data, 0, numPix * sizeof(tgt::vec2));

	/* THESE LINES DIRECTLY COPIED FROM ABOVE */
	tgt::vec2 s1(dim.x*0.40, dim.y*0.70);
	tgt::vec2 s2(dim.x*0.28, dim.y*0.36);
	float maxv = 0;
	float minv = 10000;
	float len;
	tgt::vec2 value, v1, v2;
	int index;
	for(int x=0; x<dim.x; x++){
		for(int y=0; y<dim.y; y++){
			v1 = tgt::vec2(s1.x-x, s1.y-y);
			v2 = tgt::vec2(s2.x-x, s2.y-y);
			value = tgt::vec2(0.0, 0.0);
			value += calcVec(v1, ATTRACTING_FOCUS);
			value += calcVec(v2, REPELLING_FOCUS);
			len = sqrtf(tgt::dot(value, value));
			if(len > maxv){ maxv = len; } if(len < minv){ minv = len; }
			index = y*dim.x + x;
			data[index] = value;
		}
	}
	float range = maxv - minv;
	for(int i = 0; i < numPix; ++i){ data[i] = tgt::vec2((data[i].x-minv)/range, (data[i].y-minv)/range); }
	/* END */

	tgt::vec2 thresh(0.0, 0.0);

	GLubyte* result = Helper::renderArrowPlotTexture(data, dim, privatePort1_, thresh, 15, 1);
	delete data;
	return result;

	// SPACING: 1, SIZE: 15 is good.
	//return Helper::renderArrowPlotTexture(data, dim, privatePort1_, thresh, 15, 1);
}
GLubyte* MFTGSliceRenderer::TEMPstreamLines(tgt::vec2 dim){
	int numPix = dim.x*dim.y;
	tgt::vec2* data = new tgt::vec2[numPix];
	memset(data, 0, numPix * sizeof(tgt::vec2));

	/* THESE LINES DIRECTLY COPIED FROM ABOVE */
	tgt::vec2 s1(dim.x*0.30, dim.y*0.82);
	tgt::vec2 s2(dim.x*0.8, dim.y*0.4);
	float maxv = 0;
	float minv = 10000;
	float len;
	tgt::vec2 value, v1, v2;
	int index;
	for(int x=0; x<dim.x; x++){
		for(int y=0; y<dim.y; y++){
			v1 = tgt::vec2(s1.x-x, s1.y-y);
			v2 = tgt::vec2(s2.x-x, s2.y-y);
			value = tgt::vec2(0.0, 0.0);
			value += calcVec(v1, ATTRACTING_FOCUS);
			value += calcVec(v2, ATTRACTING_FOCUS);
			len = sqrtf(tgt::dot(value, value));
			if(len > maxv){ maxv = len; } if(len < minv){ minv = len; }
			index = y*dim.x + x;
			data[index] = value;
		}
	}
	float range = maxv - minv;
	for(int i = 0; i < numPix; ++i){ data[i] = tgt::vec2((data[i].x-minv)/range, (data[i].y-minv)/range); }
	/* END */

	GLubyte* result = Helper::renderStreamlinesTexture(data, dim, 300);
	delete data;
	return result;

	// SPACING: 1, SIZE: 15 is good.
	//return Helper::renderStreamlinesTexture(data, dim, 300);
}



void MFTGSliceRenderer::mouseEvent(tgt::MouseEvent* e) {
	// see modules/base/processors/utility/distancemeasure.cpp for an example implementation of capturing mouse events.
    if (e->action() & tgt::MouseEvent::PRESSED) {
    	mouseDown_ = true;
    	counter_ = 0;
    }
    if (e->action() & tgt::MouseEvent::RELEASED) {
    	mouseDown_ = false;
    	counter_ = 0;
    }

    mouseCurPos2D_ = tgt::ivec2( tgt::ivec2(e->coord().x, e->viewport().y-e->coord().y) );
    e->accept();

    // CCVE: haxx to make sure it runs smoothly on my lab comp - if we're just moving the mouse,
    //		 only update once every 3 triggers. Also update on every click and release.
    counter_ -= 1;
    if(counter_ <= 0){
    	counter_ = ORIGINAL_COUNTER;
    	process();
    }
}

void MFTGSliceRenderer::rightClickEvent(tgt::MouseEvent* e){
    if (e->action() & tgt::MouseEvent::RELEASED) {
    	current_field += 1;
    }
    if(current_field < 1){ current_field = total_fields; }
    if(current_field > total_fields){ current_field = 1; }

	counter_ = ORIGINAL_COUNTER;
	process();
}


}
